package com.henu.engineer.service

import com.henu.engineer.dao.entity.student.StudentScoreDtl
import com.henu.engineer.dao.mapper.cultivatingproject.GraduationRequirementXmlMapper
import com.henu.engineer.dao.mapper.cultivatingproject.RequirementIndicatorXmlMapper
import com.henu.engineer.dao.mapper.student.StudentScoreDtlXmlMapper
import com.henu.engineer.dao.mapper.teachingoutline.CourseGoalXmlMapper
import com.henu.engineer.dao.mapper.teachingoutline.CourseMethodXmlMapper
import com.henu.engineer.dao.mapper.teachingoutline.CourseTeachXmlMapper
import com.henu.engineer.dao.mapper.teachingoutline.GoalMethodProportionXmlMapper
import freemarker.template.Configuration
import org.apache.poi.ss.usermodel.Cell
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.apache.poi.xwpf.usermodel.ParagraphAlignment
import org.apache.poi.xwpf.usermodel.XWPFDocument
import org.apache.poi.xwpf.usermodel.XWPFTableCell
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.util.ClassUtils
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.multipart.MultipartFile
import java.io.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.collections.ArrayList

/**
 *
 * @author noexcept
 * @since 2021/8/12 14:51
 */
@Service
class FileService {

    @Autowired
    lateinit var studentScoreDtlXmlMapper: StudentScoreDtlXmlMapper

    @Autowired
    lateinit var courseTeachXmlMapper: CourseTeachXmlMapper

    @Autowired
    lateinit var courseGoalXmlMapper: CourseGoalXmlMapper

    @Autowired
    lateinit var graduationRequirementXmlMapper: GraduationRequirementXmlMapper

    @Autowired
    lateinit var requirementIndicatorXmlMapper: RequirementIndicatorXmlMapper

    @Autowired
    lateinit var courseMethodXmlMapper: CourseMethodXmlMapper

    @Autowired
    lateinit var goalMethodProportionXmlMapper: GoalMethodProportionXmlMapper

    val logger: Logger = LoggerFactory.getLogger(javaClass)

    /**
     * 在类路径的generatedFile文件夹下生成一个文件
     * @param templateFileName 使用什么样的word文档模板，是一个类路径下fileTemplates下面的一个ftl文件的文件名，例：courseOutlineTemplates.ftl
     * @param dataMap word模板使用的数据
     * @param fileName fileName + LocalDateTime.toString() + ".doc"
     */
    fun generateWord(
        templateFileName: String = "courseOutlineTemplates.ftl",
        dataMap: Map<String, Any>,
        fileName: String = "generatedFile",
    ): File {
        val classpath = ClassUtils.getDefaultClassLoader()?.getResource("")?.path.toString()
        val dateTime = LocalDateTime.now()
        val formattedTime = DateTimeFormatter.ofPattern(" yyyy-MM-dd-HHmm").format(dateTime)

        val path = classpath + "generatedFile" + File.separator + fileName + formattedTime + ".doc"
        logger.debug("path:", path)

        val configuration = Configuration().apply {
            defaultEncoding = "UTF-8"
            setDirectoryForTemplateLoading(File(classpath + "fileTemplates" + File.separator))
        }

        logger.debug(classpath + "fileTemplates" + File.separator)

        val template = configuration.getTemplate("courseOutlineTemplates.ftl", "UTF-8")
        val file = File(path)
        if (!file.exists()) {
            file.createNewFile()
        }
        val out = BufferedWriter(OutputStreamWriter(FileOutputStream(file)))
        template.process(dataMap, out)
        out.flush()
        out.close()
        return File(path)
    }



    /**
     * 生成课程质量报告
     * @return 第一个值表示是否成功，第二个值表示错误信息
     */
    fun generateCourseQualityReport(courseId: String): Pair<Boolean, String> {
        val doc = XWPFDocument()

        val out = FileOutputStream(File("generatedWord.docx"))
        //获取课程信息
        val courseTeach = courseTeachXmlMapper.selectCourseTeachByCourseId(courseId) ?: return Pair(false, "课程id查询不到")
        var errorMsg = ""

        //设置标题
        var paragraph = doc.createParagraph()
        paragraph.alignment = ParagraphAlignment.CENTER
        var xwpfRun = paragraph.createRun()
        xwpfRun.setText("${courseTeach.name}课程质量评价报告")

        //生成课程简述
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.setText("${courseTeach.name}课程由课程负责人和任课教师按照课程实施周期（1学年）开展评价，对教学大纲、作业、试卷等教学过程资料进行分析，通过对课程的考试、作业、实验等环节设置的考查点与毕业要求中对知识、能力和素质培养等方面的吻合性、成绩分布的合理性、考核难易程度、试卷抽样分析等进行分析和评价，以学习效果作为评价课程质量的主要依据。")

        //小标题
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.isBold = true
        xwpfRun.setText("1)\t课程目标对毕业要求的支撑\n")

        //表1 课程目标对毕业要求的支撑
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.setText("表1 课程目标对毕业要求的支撑")

        //表2 课程考核方式及依据
        paragraph = doc.createParagraph()
        paragraph.alignment = ParagraphAlignment.CENTER
        xwpfRun = paragraph.createRun()
        xwpfRun.setText("表2 课程考核方式及依据")
        xwpfRun.isBold = true
        xwpfRun.fontSize = 12

        val courseGoalList = courseGoalXmlMapper.selectCourseGoalsByCourseId(courseId)
        var count = 1
        courseGoalList.forEach {
            val table = doc.createTable(5, 2)

            //第一二行的列合并,相当于第二列被删除
            val cthMerge1 = CTHMerge.Factory.newInstance()
            cthMerge1.`val` = STMerge.RESTART
            table.getRow(0).getCell(0).ctTc.addNewTcPr()
            table.getRow(0).getCell(0).ctTc.tcPr.hMerge = cthMerge1;
            table.getRow(1).getCell(0).ctTc.addNewTcPr()
            table.getRow(1).getCell(0).ctTc.tcPr.hMerge = cthMerge1;

            val cthMerge2 = CTHMerge.Factory.newInstance()
            cthMerge2.`val` = STMerge.CONTINUE
            table.getRow(0).getCell(1).ctTc.addNewTcPr()
            table.getRow(0).getCell(1).ctTc.tcPr.hMerge = cthMerge2;
            table.getRow(1).getCell(1).ctTc.addNewTcPr()
            table.getRow(1).getCell(1).ctTc.tcPr.hMerge = cthMerge2;

            //表第一行
            val firstRow = table.getRow(0)
            var cell = firstRow.getCell(0)
            cell.color = "d9d9d9"

            val paragraph = cell.addParagraph()
            paragraph.alignment = ParagraphAlignment.CENTER
            var xwpfRun = paragraph.createRun()
            val newRFonts = xwpfRun.ctr.addNewRPr().addNewRFonts()
            newRFonts.cs = "FangSong"
            xwpfRun.setText("课程目标${count++}")

            //表第二行
            val secondRow = table.getRow(1)
            val secondRowCell = secondRow.getCell(0)

            val paragraph2 = secondRowCell.addParagraph()
            xwpfRun = paragraph2.createRun()
            xwpfRun.setText(it.content)

            //第三行
            val thirdRow = table.getRow(2)

            cell = thirdRow.getCell(0)
            var paragraph3 = cell.addParagraph()
            xwpfRun = paragraph3.createRun()
            xwpfRun.setText("支撑的毕业要求1")
            xwpfRun.isBold = true
            xwpfRun.fontSize = 12

            val graduationRequirement = graduationRequirementXmlMapper.selectById(it.graduationRequirement)

            cell = thirdRow.getCell(1)
            paragraph3 = cell.addParagraph()
            xwpfRun = paragraph3.createRun()
            if (graduationRequirement != null) {
                xwpfRun.setText(graduationRequirement.content)
            } else xwpfRun.setText("无数据")


            //第四行
            val fourthRow = table.getRow(3)

            //获取指标内容
            val indicator = requirementIndicatorXmlMapper.selectByIndicatorId(it.indicatorId)

            cell = fourthRow.getCell(0)
            paragraph3 = cell.addParagraph()
            xwpfRun = paragraph3.createRun()
            xwpfRun.setText("支撑的毕业要求指标点${it.indicatorId}")
            xwpfRun.fontFamily = "imitation Song"
            xwpfRun.isBold = true
            xwpfRun.fontSize = 12

            cell = fourthRow.getCell(1)
            paragraph3 = cell.addParagraph()
            xwpfRun = paragraph3.createRun()
            if (indicator != null) {
                xwpfRun.setText(indicator.content)
            } else xwpfRun.setText("查询不到数据")
            xwpfRun.fontSize = 12

            //第五行
            val fifthRow = table.getRow(4)

            cell = fifthRow.getCell(0)
            cell.verticalAlignment = XWPFTableCell.XWPFVertAlign.CENTER
            paragraph3 = cell.addParagraph()
            paragraph3.alignment = ParagraphAlignment.CENTER

            xwpfRun = paragraph3.createRun()
            xwpfRun.setText("目标达成手段")
            xwpfRun.fontFamily = "imitation Song"
            xwpfRun.isBold = true
            xwpfRun.fontSize = 12

            cell = fifthRow.getCell(1)
            paragraph3 = cell.addParagraph()
            xwpfRun = paragraph3.createRun()
            xwpfRun.setText(it.measures)
            xwpfRun.fontFamily = "imitation Song"
            xwpfRun.fontSize = 12
        }

        //小标题
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.isBold = true
        xwpfRun.setText("2)\t教学过程对教学目标的支撑\n")

        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.setText("按照教学大纲，结合课时分布，分别对每次课的教学内容和教学目标进行规划设计，以课堂讲授、课堂提问、分组讨论、项目阶段评审、课后作业、书面报告、团队项目实践等方式，结合案例分析、项目驱动、研讨式教学等多种教学方法，着力培养学生采用工程化的方法来设计与开发软件，使学生能够对复杂工程问题进行分析，设计软件开发解决方案，提高了学生解决复杂工程问题的能力，同时培养学生的社会责任意识、团队合作精神、沟通能力和项目综合管理的能力，全面覆盖了所支撑的毕业要求。")

        //小标题:考查环节对教学目标的支撑
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.isBold = true
        xwpfRun.setText("3)\t考查环节对教学目标的支撑\n")

        //获取课程考核方式列表
        val courseMethodList = courseMethodXmlMapper.selectCourseMethodByCourseId(courseId)
        if (courseMethodList.isEmpty()) {
            return Pair(false, "课程考核方式为空")
        }

        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        var s =
            "按照教学内容安排及教学目标，设计一系列的考查环节，并制定对应评价标准。由于软件工程课程是一门实践性较强的课程，学生不仅要具有现代软件工程开发能力，也要具备主动学习、协作学习、系统思维、过程控制、表达展示等综合素质。因此本课程采用过程性评价的考核机制，不以考试成绩作为唯一的考核目标。课程考核方式包括"
        for (i in (0..courseMethodList.size - 2))
            s += "${courseMethodList[i].method}(${courseMethodList[i].proportion * 100}%),"
        s += "和${courseMethodList.last().method}(${courseMethodList.last().proportion * 100}%),"
        xwpfRun.setText(s)

        //课程考核方式及依据如表2所示。
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.setText("课程考核方式及依据如表2所示。")

        //表2 课程考核方式及依据
        paragraph = doc.createParagraph()
        paragraph.alignment = ParagraphAlignment.CENTER
        xwpfRun = paragraph.createRun()
        xwpfRun.setText("表2 课程考核方式及依据")
        xwpfRun.isBold = true
        xwpfRun.fontSize = 12

        var table = doc.createTable(courseMethodList.size + 1, 4)
        //设置表头
        var rowIterator = table.rows.iterator()
        var row0 = rowIterator.next()
        row0.getCell(0).text = "考核方式"
        row0.getCell(1).text = "考核方式占比"
        row0.getCell(2).text = "考核方式内容及要求"
        row0.getCell(3).text = "成绩评定指标与依据"
        courseMethodList.forEach {
            val row = rowIterator.next()
            val cell0 = row.getCell(0)
            cell0.verticalAlignment = XWPFTableCell.XWPFVertAlign.CENTER
            var paragraph1 = cell0.addParagraph()
            var xwpfRun1 = paragraph1.createRun()
            xwpfRun1.setText(it.method)

            val cell1 = row.getCell(1)
            cell1.verticalAlignment = XWPFTableCell.XWPFVertAlign.CENTER
            paragraph1 = cell1.addParagraph()
            xwpfRun1 = paragraph1.createRun()
            xwpfRun1.setText("${it.proportion * 100}%")

            val cell2 = row.getCell(2)
            cell2.verticalAlignment = XWPFTableCell.XWPFVertAlign.CENTER
            paragraph1 = cell2.addParagraph()
            xwpfRun1 = paragraph1.createRun()
            xwpfRun1.setText(it.content)

            val cell3 = row.getCell(3)
            cell3.verticalAlignment = XWPFTableCell.XWPFVertAlign.CENTER
            paragraph1 = cell3.addParagraph()
            xwpfRun1 = paragraph1.createRun()
            xwpfRun1.setText(it.basis)
        }

        //表3 考核方式及占比
        paragraph = doc.createParagraph()
        paragraph.alignment = ParagraphAlignment.CENTER
        xwpfRun = paragraph.createRun()
        xwpfRun.setText("表3 考核方式及占比")
        xwpfRun.isBold = true
        xwpfRun.fontSize = 12

        table = doc.createTable(courseGoalList.size * 3 + 1, 3)
        rowIterator = table.rows.iterator()
        row0 = rowIterator.next()
        row0.getCell(0).text = "课程目标"
        row0.getCell(1).text = "目标占比"
        row0.getCell(2).text = "考核方式占比"

        for (i in courseGoalList.indices) {
            val row1 = table.getRow(i * 3 + 1)
            val row2 = table.getRow(i * 3 + 2)
            val row3 = table.getRow(i * 3 + 3)

            val ctvMerge1 = CTVMerge.Factory.newInstance()
            ctvMerge1.`val` = STMerge.RESTART
            val cell00 = row1.getCell(0)
            cell00.ctTc.addNewTcPr()
            cell00.ctTc.tcPr.vMerge = ctvMerge1
            val cell01 = row1.getCell(1)
            cell01.ctTc.addNewTcPr()
            cell01.ctTc.tcPr.vMerge = ctvMerge1


            val ctvMerge2 = CTVMerge.Factory.newInstance()
            ctvMerge2.`val` = STMerge.CONTINUE
            val cell10 = row2.getCell(0)
            cell10.ctTc.addNewTcPr()
            cell10.ctTc.tcPr.vMerge = ctvMerge2
            val cell11 = row2.getCell(1)
            cell11.ctTc.addNewTcPr()
            cell11.ctTc.tcPr.vMerge = ctvMerge2


            val ctvMerge3 = CTVMerge.Factory.newInstance()
            ctvMerge3.`val` = STMerge.CONTINUE
            val cell20 = row3.getCell(0)
            cell20.ctTc.addNewTcPr()
            cell20.ctTc.tcPr.vMerge = ctvMerge3
            val cell21 = row3.getCell(1)
            cell21.ctTc.addNewTcPr()
            cell21.ctTc.tcPr.vMerge = ctvMerge3

            row1.getCell(0).text = "目标${i + 1}: ${courseGoalList[i].content}"
            row1.getCell(1).text = "${courseGoalList[i].proportion * 100}%"

            val methodList = goalMethodProportionXmlMapper.selectByCourseGoalId(courseGoalList[i].id)
            if (methodList.isEmpty()) {
                val error = "课程目标${courseGoalList[i].id}对应的考核方式占比无数据\n"
                errorMsg += error
                continue
            }

            row1.getCell(2).text = "${methodList[0].method}${methodList[0].proportion * 100}%"
            if (methodList.size >= 2)
                row2.getCell(2).text = "${methodList[1].method}${methodList[1].proportion * 100}%"
            if (methodList.size >= 3)
                row3.getCell(2).text = "${methodList[2].method}${methodList[2].proportion * 100}%"

        }

        //小标题:4)	考试内容对教学目标的支撑
        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.isBold = true
        xwpfRun.setText("4)\t考试内容对教学目标的支撑 \n")

        paragraph = doc.createParagraph()
        xwpfRun = paragraph.createRun()
        xwpfRun.fontSize = 12
        xwpfRun.setText("按照教学内容安排及教学目标，对试卷内容进行明确要求，并制定对应评价标准。说明每个题目对毕业要求指标点的支撑关系，相关内容包含在试卷审批表中，如表4所示。")

        //表4 考试内容对毕业要求支撑关系
        paragraph = doc.createParagraph()
        paragraph.alignment = ParagraphAlignment.CENTER
        xwpfRun = paragraph.createRun()
        xwpfRun.setText("表4 考试内容对毕业要求支撑关系")
        xwpfRun.isBold = true
        xwpfRun.fontSize = 12

        // TODO: 9/24/2021 表4 考试内容对毕业要求支撑关系的生成


        doc.write(out)
        out.close()
        return Pair(true, errorMsg)
    }

    /**
     *
     * @return 一个二元组，第一个是处理结果，第二个是文件全路径名
     */
    fun saveUploadedFile(file: MultipartFile): Pair<String, String?> {
        var res = "SUCCESS"
        val fileName = UUID.randomUUID().toString() + "_" + file.originalFilename
        val classpath = ClassUtils.getDefaultClassLoader()?.getResource("")?.path.toString()
        val dirPath = classpath + "uploadedFile" + File.separator
        val filePath = File(dirPath)
        if (!filePath.exists()) {
            filePath.mkdirs()
        }

        try {
            file.transferTo(File(dirPath + fileName))
        } catch (e: Exception) {
//            res = e.message ?: "FAILED"
            res ="FAILED"
        }
        dirPath.replace("\\", File.separator)
        dirPath.replace("/", File.separator)

        return Pair(res, dirPath + fileName)
    }



    /**
     *
     * 学生得分明细表解析
     * 课程负责人导入
     * 学生得分明细表上传之后，对其进行解析，存入学生试卷得分明细表（student_score_dtl）
     * 得分明细表：每个学生的期末考试的每道题的得分
     * 文件需要在classpath下的uploadedFile文件夹内,文件后缀名为xls或xlsx
     */
    fun parseUploadedExcelFileOfStudentScoreDtl(fileName: String, courseId: String, testPaperId: String): String {
        val file = File(fileName)
        val fileInputStream = FileInputStream(file)

        val parseRes = HashMap<String, List<StudentScoreDtl>>()
        var errorMsg = ""

        val wb = XSSFWorkbook(fileInputStream)
        val sheet = wb.getSheetAt(0)
        val rowIterator = sheet.iterator()

        //表头
        val firstRow = rowIterator.next()

        //遍历每一行
        while (rowIterator.hasNext()) {
            val row = rowIterator.next()
            val cellIterator = row.iterator()

            //获取学号
            val cellStudentId = cellIterator.next()
            var studentId = when (cellStudentId.cellType) {
                Cell.CELL_TYPE_NUMERIC -> cellStudentId.numericCellValue.toString().trim()
                Cell.CELL_TYPE_STRING -> cellStudentId.stringCellValue.trim()
                else -> {
                    errorMsg += "第${row.rowNum}行无法解析\n"
                    ""
                }
            }
            if (studentId == "")
                continue
            val scores = ArrayList<StudentScoreDtl>()
            parseRes[studentId] = scores

            //跳过姓名
            cellIterator.next()

            //存入各题分数
            val firstRowCellIterator = firstRow.iterator()
            //跳过学号和姓名表头
            firstRowCellIterator.next()
            firstRowCellIterator.next()
            while (cellIterator.hasNext()) {
                val questionNumber = firstRowCellIterator.next().stringCellValue
                if (questionNumber == "") {
                    break;
                }
                val score = cellIterator.next().numericCellValue
                scores.add(StudentScoreDtl(studentId, null, testPaperId, questionNumber, courseId, score))
            }
        }

        //存入数据库
        parseRes.forEach {
            studentScoreDtlXmlMapper.insertList(it.value)
        }

        return errorMsg

    }

    /**
     * 每个课程的每个考核方式的每个目标得分EXcel表的解析
     * 解析结果存入student_course_method
     */
    fun parseUploadedExcelFileOfMethodScore(fileName: String, courseId: String){

    }


    /**
     * 文件上传接口
     */
    fun uploadingSyllabus(file: MultipartFile,path:String):Boolean{
        try {
            //这个放到controller里面进行检测
//            if (file.isEmpty) {
//                return 1
//            }
            val dest = File(path)
            // 检测是否存在目录
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs() // 新建文件夹
            }
            file.transferTo(dest) // 文件写入
            return true
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 文件下载接口
     */



}